#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
自定义命令测试界面
Custom Command Testing Widget

功能特性：
- 手动构建和发送命令
- 十六进制数据编辑
- 命令历史记录
- 响应数据解析
- 常用命令模板
"""

import json
import re
from datetime import datetime
from typing import List, Dict, Optional

from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, 
                           QGridLayout, QLabel, QLineEdit, QPushButton, 
                           QTextEdit, QComboBox, QSpinBox, QTableWidget, 
                           QTableWidgetItem, QTabWidget, QSplitter, 
                           QMessageBox, QFileDialog, QCheckBox, QFrame)
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
from PyQt5.QtGui import QFont, QTextCursor, QColor

from serial_handler import NSA5312CommandHelper, CommandResponse
from serial_protocol import NSA5312Protocol, CommandCode

class HexLineEdit(QLineEdit):
    """十六进制输入控件"""
    
    def __init__(self, max_bytes: int = 32):
        super().__init__()
        self.max_bytes = max_bytes
        self.setPlaceholderText("输入十六进制数据，如：01 02 03 或 010203")
        self.textChanged.connect(self.validate_hex)
    
    def validate_hex(self):
        """验证十六进制输入"""
        text = self.text().replace(" ", "").replace("0x", "").replace("0X", "")
        
        # 只允许十六进制字符
        if not re.match(r'^[0-9A-Fa-f]*$', text):
            # 移除非十六进制字符
            text = re.sub(r'[^0-9A-Fa-f]', '', text)
            self.setText(text)
            return
        
        # 限制最大字节数
        if len(text) > self.max_bytes * 2:
            text = text[:self.max_bytes * 2]
            self.setText(text)
    
    def get_bytes(self) -> bytes:
        """获取字节数据"""
        text = self.text().replace(" ", "").replace("0x", "").replace("0X", "")
        
        # 确保偶数长度
        if len(text) % 2 == 1:
            text = "0" + text
        
        try:
            return bytes.fromhex(text)
        except ValueError:
            return b''
    
    def set_bytes(self, data: bytes):
        """设置字节数据"""
        hex_str = ' '.join(f'{b:02X}' for b in data)
        self.setText(hex_str)

class CommandHistoryWidget(QWidget):
    """命令历史记录控件"""
    
    command_selected = pyqtSignal(int, bytes)  # (command_code, parameters)
    
    def __init__(self):
        super().__init__()
        self.history = []
        self.init_ui()
    
    def init_ui(self):
        """初始化UI"""
        layout = QVBoxLayout()
        
        # 控制按钮
        button_layout = QHBoxLayout()
        
        clear_btn = QPushButton("清空历史")
        clear_btn.clicked.connect(self.clear_history)
        button_layout.addWidget(clear_btn)
        
        save_btn = QPushButton("保存历史")
        save_btn.clicked.connect(self.save_history)
        button_layout.addWidget(save_btn)
        
        load_btn = QPushButton("加载历史")
        load_btn.clicked.connect(self.load_history)
        button_layout.addWidget(load_btn)
        
        button_layout.addStretch()
        layout.addLayout(button_layout)
        
        # 历史记录表格
        self.history_table = QTableWidget()
        self.history_table.setColumnCount(5)
        self.history_table.setHorizontalHeaderLabels(["时间", "命令", "参数", "状态", "响应"])
        self.history_table.cellDoubleClicked.connect(self.on_item_double_clicked)
        layout.addWidget(self.history_table)
        
        self.setLayout(layout)
    
    def add_command(self, command_code: int, parameters: bytes, response: CommandResponse):
        """添加命令到历史记录"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        
        # 添加到历史列表
        history_item = {
            "timestamp": timestamp,
            "command_code": command_code,
            "parameters": parameters.hex(),
            "response": response
        }
        self.history.append(history_item)
        
        # 更新表格
        row = self.history_table.rowCount()
        self.history_table.insertRow(row)
        
        self.history_table.setItem(row, 0, QTableWidgetItem(timestamp))
        self.history_table.setItem(row, 1, QTableWidgetItem(f"0x{command_code:02X}"))
        self.history_table.setItem(row, 2, QTableWidgetItem(parameters.hex().upper()))
        
        # 状态颜色
        status_item = QTableWidgetItem("成功" if response.success else "失败")
        if response.success:
            status_item.setBackground(QColor(144, 238, 144))  # 浅绿色
        else:
            status_item.setBackground(QColor(255, 182, 193))  # 浅红色
        self.history_table.setItem(row, 3, status_item)
        
        # 响应数据
        response_data = response.data.hex().upper() if response.data else ""
        self.history_table.setItem(row, 4, QTableWidgetItem(response_data))
        
        # 自动滚动到底部
        self.history_table.scrollToBottom()
        
        # 限制历史记录数量
        if len(self.history) > 1000:
            self.history = self.history[-1000:]
            self.history_table.removeRow(0)
    
    def on_item_double_clicked(self, row: int, column: int):
        """双击历史记录项"""
        if row < len(self.history):
            item = self.history[row]
            command_code = item["command_code"]
            parameters = bytes.fromhex(item["parameters"])
            self.command_selected.emit(command_code, parameters)
    
    def clear_history(self):
        """清空历史记录"""
        self.history.clear()
        self.history_table.setRowCount(0)
    
    def save_history(self):
        """保存历史记录"""
        filename, _ = QFileDialog.getSaveFileName(
            self, "保存命令历史", "", "JSON Files (*.json);;All Files (*)"
        )
        
        if filename:
            try:
                # 转换为可序列化的格式
                serializable_history = []
                for item in self.history:
                    serializable_item = {
                        "timestamp": item["timestamp"],
                        "command_code": item["command_code"],
                        "parameters": item["parameters"],
                        "response": {
                            "success": item["response"].success,
                            "command": item["response"].command,
                            "status": item["response"].status,
                            "data": item["response"].data.hex(),
                            "error_msg": item["response"].error_msg
                        }
                    }
                    serializable_history.append(serializable_item)
                
                with open(filename, 'w', encoding='utf-8') as f:
                    json.dump(serializable_history, f, indent=2, ensure_ascii=False)
                
                QMessageBox.information(self, "成功", f"历史记录已保存到: {filename}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存历史记录失败: {str(e)}")
    
    def load_history(self):
        """加载历史记录"""
        filename, _ = QFileDialog.getOpenFileName(
            self, "加载命令历史", "", "JSON Files (*.json);;All Files (*)"
        )
        
        if filename:
            try:
                with open(filename, 'r', encoding='utf-8') as f:
                    history_data = json.load(f)
                
                # 清空当前历史
                self.clear_history()
                
                # 重建历史记录
                for item in history_data:
                    response = CommandResponse(
                        success=item["response"]["success"],
                        command=item["response"]["command"],
                        status=item["response"]["status"],
                        data=bytes.fromhex(item["response"]["data"]),
                        error_msg=item["response"]["error_msg"]
                    )
                    
                    command_code = item["command_code"]
                    parameters = bytes.fromhex(item["parameters"])
                    
                    self.add_command(command_code, parameters, response)
                
                QMessageBox.information(self, "成功", f"历史记录已从 {filename} 加载")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"加载历史记录失败: {str(e)}")

class CommandTemplateWidget(QWidget):
    """命令模板控件"""
    
    template_selected = pyqtSignal(int, bytes)  # (command_code, parameters)
    
    def __init__(self):
        super().__init__()
        self.templates = self.get_default_templates()
        self.init_ui()
    
    def init_ui(self):
        """初始化UI"""
        layout = QVBoxLayout()
        
        # 模板选择
        template_layout = QHBoxLayout()
        
        template_layout.addWidget(QLabel("模板:"))
        self.template_combo = QComboBox()
        self.template_combo.currentTextChanged.connect(self.on_template_changed)
        template_layout.addWidget(self.template_combo)
        
        use_template_btn = QPushButton("使用模板")
        use_template_btn.clicked.connect(self.use_template)
        template_layout.addWidget(use_template_btn)
        
        template_layout.addStretch()
        layout.addLayout(template_layout)
        
        # 模板描述
        self.template_description = QTextEdit()
        self.template_description.setMaximumHeight(100)
        self.template_description.setReadOnly(True)
        layout.addWidget(self.template_description)
        
        self.setLayout(layout)
        
        # 填充模板列表
        self.populate_templates()
    
    def get_default_templates(self) -> Dict[str, Dict]:
        """获取默认模板"""
        return {
            "获取版本信息": {
                "command": 0x01,
                "parameters": b'',
                "description": "获取设备固件版本信息"
            },
            "读取ADC通道0": {
                "command": 0x30,
                "parameters": b'\x00',
                "description": "读取ADC通道0的值"
            },
            "64通道ADC采集": {
                "command": 0x40,
                "parameters": b'',
                "description": "采集64通道ADC数据"
            },
            "发送64通道ADC数据": {
                "command": 0x41,
                "parameters": b'',
                "description": "发送已采集的64通道ADC数据"
            },
            "GPIO行控制": {
                "command": 0x0A,
                "parameters": b'\x00\x01',
                "description": "控制GPIO行，通道0，值1"
            },
            "GPIO列控制": {
                "command": 0x0B,
                "parameters": b'\x00\x01',
                "description": "控制GPIO列，通道0，值1"
            },
            "I2C写入测试": {
                "command": 0x10,
                "parameters": b'\x50\x00\x55\xAA',
                "description": "I2C写入测试，设备地址0x50，寄存器0x00，数据0x55AA"
            },
            "I2C读取测试": {
                "command": 0x11,
                "parameters": b'\x50\x00\x02',
                "description": "I2C读取测试，设备地址0x50，寄存器0x00，读取2字节"
            },
            "SPI写入测试": {
                "command": 0x12,
                "parameters": b'\x04\x01\x02\x03\x04',
                "description": "SPI写入测试，发送4字节数据"
            },
            "OWI写入测试": {
                "command": 0x1A,
                "parameters": b'\x03\x01\x02\x03',
                "description": "OWI写入测试，发送3字节数据"
            },
            "OWI校准模式": {
                "command": 0x23,
                "parameters": b'',
                "description": "进入OWI校准模式"
            },
            "电源控制": {
                "command": 0x20,
                "parameters": b'\x00\x01',
                "description": "8通道电源控制，通道0，开启"
            },
            "电压切换": {
                "command": 0x21,
                "parameters": b'\x00',
                "description": "5V/6.5V电压切换，选择5V"
            },
            "接口切换": {
                "command": 0x22,
                "parameters": b'\x00',
                "description": "COM/OWI/AOUT接口切换"
            }
        }
    
    def populate_templates(self):
        """填充模板列表"""
        self.template_combo.clear()
        self.template_combo.addItems(list(self.templates.keys()))
        
        if self.templates:
            self.on_template_changed(list(self.templates.keys())[0])
    
    def on_template_changed(self, template_name: str):
        """模板选择变化"""
        if template_name in self.templates:
            template = self.templates[template_name]
            self.template_description.setText(template["description"])
    
    def use_template(self):
        """使用选中的模板"""
        template_name = self.template_combo.currentText()
        if template_name in self.templates:
            template = self.templates[template_name]
            self.template_selected.emit(template["command"], template["parameters"])

class CustomCommandWidget(QWidget):
    """自定义命令测试控件"""
    
    def __init__(self, command_helper: NSA5312CommandHelper):
        super().__init__()
        self.command_helper = command_helper
        self.protocol = NSA5312Protocol()
        self.init_ui()
    
    def init_ui(self):
        """初始化UI"""
        layout = QVBoxLayout()
        
        # 创建分割器
        splitter = QSplitter(Qt.Horizontal)
        
        # 左侧：命令构建区域
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)
        
        # 命令构建组
        command_group = QGroupBox("命令构建")
        command_layout = QGridLayout()
        
        # 命令码输入
        command_layout.addWidget(QLabel("命令码:"), 0, 0)
        self.command_spin = QSpinBox()
        self.command_spin.setRange(0, 255)
        self.command_spin.setValue(0x01)
        self.command_spin.valueChanged.connect(self.update_command_info)
        command_layout.addWidget(self.command_spin, 0, 1)
        
        # 十六进制输入
        hex_input_layout = QHBoxLayout()
        hex_input_layout.addWidget(QLabel("0x"))
        self.command_hex_edit = QLineEdit()
        self.command_hex_edit.setMaxLength(2)
        self.command_hex_edit.setText("01")
        self.command_hex_edit.textChanged.connect(self.on_hex_command_changed)
        hex_input_layout.addWidget(self.command_hex_edit)
        command_layout.addLayout(hex_input_layout, 0, 2)
        
        # 命令名称显示
        command_layout.addWidget(QLabel("命令名称:"), 1, 0)
        self.command_name_label = QLabel("GET_VERSION")
        command_layout.addWidget(self.command_name_label, 1, 1, 1, 2)
        
        # 参数输入
        command_layout.addWidget(QLabel("参数:"), 2, 0)
        self.parameters_edit = HexLineEdit()
        command_layout.addWidget(self.parameters_edit, 2, 1, 1, 2)
        
        # 完整帧显示
        command_layout.addWidget(QLabel("完整帧:"), 3, 0)
        self.frame_display = QLineEdit()
        self.frame_display.setReadOnly(True)
        command_layout.addWidget(self.frame_display, 3, 1, 1, 2)
        
        # 更新完整帧按钮
        update_frame_btn = QPushButton("更新帧")
        update_frame_btn.clicked.connect(self.update_frame_display)
        command_layout.addWidget(update_frame_btn, 4, 0)
        
        # 发送按钮
        send_btn = QPushButton("发送命令")
        send_btn.clicked.connect(self.send_command)
        command_layout.addWidget(send_btn, 4, 1)
        
        # 清空按钮
        clear_btn = QPushButton("清空")
        clear_btn.clicked.connect(self.clear_command)
        command_layout.addWidget(clear_btn, 4, 2)
        
        command_group.setLayout(command_layout)
        left_layout.addWidget(command_group)
        
        # 响应显示组
        response_group = QGroupBox("响应数据")
        response_layout = QVBoxLayout()
        
        # 响应状态
        status_layout = QHBoxLayout()
        status_layout.addWidget(QLabel("状态:"))
        self.status_label = QLabel("等待发送")
        status_layout.addWidget(self.status_label)
        status_layout.addStretch()
        response_layout.addLayout(status_layout)
        
        # 原始响应数据
        response_layout.addWidget(QLabel("原始数据:"))
        self.raw_response_edit = QLineEdit()
        self.raw_response_edit.setReadOnly(True)
        response_layout.addWidget(self.raw_response_edit)
        
        # 解析后的数据
        response_layout.addWidget(QLabel("解析数据:"))
        self.parsed_response_text = QTextEdit()
        self.parsed_response_text.setMaximumHeight(100)
        self.parsed_response_text.setReadOnly(True)
        response_layout.addWidget(self.parsed_response_text)
        
        response_group.setLayout(response_layout)
        left_layout.addWidget(response_group)
        
        # 自动发送组
        auto_send_group = QGroupBox("自动发送")
        auto_send_layout = QGridLayout()
        
        # 启用自动发送
        self.auto_send_checkbox = QCheckBox("启用自动发送")
        self.auto_send_checkbox.toggled.connect(self.toggle_auto_send)
        auto_send_layout.addWidget(self.auto_send_checkbox, 0, 0)
        
        # 发送间隔
        auto_send_layout.addWidget(QLabel("间隔(ms):"), 0, 1)
        self.interval_spin = QSpinBox()
        self.interval_spin.setRange(100, 10000)
        self.interval_spin.setValue(1000)
        auto_send_layout.addWidget(self.interval_spin, 0, 2)
        
        # 发送计数
        auto_send_layout.addWidget(QLabel("已发送:"), 1, 0)
        self.send_count_label = QLabel("0")
        auto_send_layout.addWidget(self.send_count_label, 1, 1)
        
        # 成功/失败计数
        auto_send_layout.addWidget(QLabel("成功/失败:"), 1, 2)
        self.success_count_label = QLabel("0/0")
        auto_send_layout.addWidget(self.success_count_label, 1, 3)
        
        auto_send_group.setLayout(auto_send_layout)
        left_layout.addWidget(auto_send_group)
        
        left_layout.addStretch()
        splitter.addWidget(left_widget)
        
        # 右侧：历史记录和模板
        right_widget = QWidget()
        right_layout = QVBoxLayout(right_widget)
        
        # 创建选项卡
        tab_widget = QTabWidget()
        
        # 命令模板选项卡
        self.template_widget = CommandTemplateWidget()
        self.template_widget.template_selected.connect(self.use_template)
        tab_widget.addTab(self.template_widget, "命令模板")
        
        # 历史记录选项卡
        self.history_widget = CommandHistoryWidget()
        self.history_widget.command_selected.connect(self.use_history_command)
        tab_widget.addTab(self.history_widget, "历史记录")
        
        right_layout.addWidget(tab_widget)
        splitter.addWidget(right_widget)
        
        # 设置分割器比例
        splitter.setSizes([400, 600])
        
        layout.addWidget(splitter)
        self.setLayout(layout)
        
        # 自动发送定时器
        self.auto_send_timer = QTimer()
        self.auto_send_timer.timeout.connect(self.auto_send_command)
        
        # 计数器
        self.send_count = 0
        self.success_count = 0
        self.failure_count = 0
        
        # 初始化显示
        self.update_command_info()
        self.update_frame_display()
    
    def update_command_info(self):
        """更新命令信息"""
        command_code = self.command_spin.value()
        
        # 更新十六进制显示
        self.command_hex_edit.setText(f"{command_code:02X}")
        
        # 更新命令名称
        command_name = "未知命令"
        for cmd in CommandCode:
            if cmd.value == command_code:
                command_name = cmd.name
                break
        
        self.command_name_label.setText(command_name)
        
        # 更新完整帧
        self.update_frame_display()
    
    def on_hex_command_changed(self):
        """十六进制命令码变化"""
        hex_text = self.command_hex_edit.text()
        try:
            command_code = int(hex_text, 16)
            if 0 <= command_code <= 255:
                self.command_spin.setValue(command_code)
        except ValueError:
            pass
    
    def update_frame_display(self):
        """更新完整帧显示"""
        try:
            command_code = self.command_spin.value()
            parameters = self.parameters_edit.get_bytes()
            
            # 构建完整帧
            frame = self.protocol.build_custom_command(command_code, parameters)
            
            # 显示十六进制
            hex_str = ' '.join(f'{b:02X}' for b in frame)
            self.frame_display.setText(hex_str)
            
        except Exception as e:
            self.frame_display.setText(f"帧构建错误: {str(e)}")
    
    def send_command(self):
        """发送命令"""
        try:
            command_code = self.command_spin.value()
            parameters = self.parameters_edit.get_bytes()
            
            # 发送命令
            response = self.command_helper.send_custom_command(command_code, parameters)
            
            # 更新计数
            self.send_count += 1
            if response.success:
                self.success_count += 1
                self.status_label.setText("发送成功")
                self.status_label.setStyleSheet("color: green; font-weight: bold;")
            else:
                self.failure_count += 1
                self.status_label.setText("发送失败")
                self.status_label.setStyleSheet("color: red; font-weight: bold;")
            
            # 更新显示
            self.update_counters()
            
            # 显示响应数据
            self.display_response(response)
            
            # 添加到历史记录
            self.history_widget.add_command(command_code, parameters, response)
            
        except Exception as e:
            self.status_label.setText(f"发送异常: {str(e)}")
            self.status_label.setStyleSheet("color: red; font-weight: bold;")
    
    def display_response(self, response: CommandResponse):
        """显示响应数据"""
        # 显示原始数据
        if response.data:
            hex_str = ' '.join(f'{b:02X}' for b in response.data)
            self.raw_response_edit.setText(hex_str)
        else:
            self.raw_response_edit.setText("无数据")
        
        # 显示解析后的数据
        parsed_text = f"成功: {response.success}\n"
        parsed_text += f"命令码: 0x{response.command:02X}\n"
        parsed_text += f"状态码: 0x{response.status:02X}\n"
        parsed_text += f"数据长度: {len(response.data)} 字节\n"
        
        if response.error_msg:
            parsed_text += f"错误信息: {response.error_msg}\n"
        
        # 尝试解析特定数据
        if response.data:
            if response.command == CommandCode.GET_VERSION.value and len(response.data) >= 1:
                version = response.data[0]
                parsed_text += f"版本信息: {version}\n"
            elif response.command == CommandCode.ADC_SINGLE_READ.value and len(response.data) >= 2:
                adc_value = int.from_bytes(response.data[:2], byteorder='big')
                parsed_text += f"ADC值: {adc_value}\n"
            elif len(response.data) >= 2:
                # 尝试解析为温度数据
                try:
                    temp_raw = int.from_bytes(response.data[:2], byteorder='big')
                    if temp_raw & 0x8000:
                        temp_raw = temp_raw - 65536
                    temperature = (temp_raw * 0.077) + 51.6
                    parsed_text += f"温度值: {temperature:.1f}°C\n"
                except:
                    pass
        
        self.parsed_response_text.setText(parsed_text)
    
    def clear_command(self):
        """清空命令"""
        self.command_spin.setValue(0x01)
        self.parameters_edit.clear()
        self.raw_response_edit.clear()
        self.parsed_response_text.clear()
        self.status_label.setText("等待发送")
        self.status_label.setStyleSheet("")
        self.update_frame_display()
    
    def use_template(self, command_code: int, parameters: bytes):
        """使用模板"""
        self.command_spin.setValue(command_code)
        self.parameters_edit.set_bytes(parameters)
        self.update_frame_display()
    
    def use_history_command(self, command_code: int, parameters: bytes):
        """使用历史命令"""
        self.command_spin.setValue(command_code)
        self.parameters_edit.set_bytes(parameters)
        self.update_frame_display()
    
    def toggle_auto_send(self, enabled: bool):
        """切换自动发送"""
        if enabled:
            interval = self.interval_spin.value()
            self.auto_send_timer.start(interval)
        else:
            self.auto_send_timer.stop()
    
    def auto_send_command(self):
        """自动发送命令"""
        self.send_command()
    
    def update_counters(self):
        """更新计数器"""
        self.send_count_label.setText(str(self.send_count))
        self.success_count_label.setText(f"{self.success_count}/{self.failure_count}")
    
    def reset_counters(self):
        """重置计数器"""
        self.send_count = 0
        self.success_count = 0
        self.failure_count = 0
        self.update_counters()